#include "Ball.h"
#include "Random.h"
#include "Sparks.h"
#include "SDLAudialObject.h"
#include <cmath>

Ball::Ball(VisualObjectProvider* vObjProv, 
		   Playfield &pl, 
		   Player &left, 
		   Player &right,
		   GameMode &gm):
	playfield(pl),
	width(20),
	height(20),
	leftPlayer(left),
	rightPlayer(right),
	gamemode(gm),
	visualObjectProvider(vObjProv)
{
    bounceWall = new SDLAudialObject("sounds/bounce1.wav");
    bounceRight = new SDLAudialObject("sounds/bounce2.wav");
    bounceLeft = new SDLAudialObject("sounds/bounce3.wav");
    good = new SDLAudialObject("sounds/good.wav");
	representation = visualObjectProvider->createVisualObject();
	representation->loadImage("images/ball.png");

	//place the ball in right position and give it a speed
	reset();
}

Ball::~Ball(void)
{
    delete good;
    delete bounceLeft;
    delete bounceRight;
    delete bounceWall;
}

void Ball::update(const double time)
{
	if(gamemode == GM_INTRO)
		updateIntro(time);
	if(gamemode == GM_PLAY)
		updatePlay(time);
}

void Ball::updateIntro(const double time)
{}

void Ball::updatePlay(const double time)
{
	realX += vx * time;
	realY += vy * time;

	// are we hitting the upper boundary?
	if(realY < getHeight()/2 && vy < 0)
    {
		vy *= -1;
        bounceWall->play();
    }

	// or the lower boundary?
	if(realY > playfield.getHeight()-getHeight()/2 && vy > 0)
    {
		vy *= -1;
        bounceWall->play();
    }

	// the left player
	if((vx < 0) && intersectsWithPlayer(leftPlayer))
	{
		vx *= -1;
		increaseSpeed();
		showSparks(PP_LEFT);
        bounceLeft->play();
	}

	// and the right player
	if((vx > 0) && intersectsWithPlayer(rightPlayer))
	{
		vx *= -1;
		increaseSpeed();
		showSparks(PP_RIGHT);
        if(++bounces >= 5)
        {
            bounces = 0;
            good->play();
        }
        bounceRight->play();
	}

	// update our little spark
	if(spark != 0)
		spark->update(time);

	// if our spark is no longer necessary, delete it
	if(spark != 0 && spark->canBeDeleted())
	{
		delete spark;
		spark = 0;
	}
}

void Ball::increaseSpeed()
{
    static const double MAX_SPEED = 1000;

    if (vy >= 0 && vy < 50)
        vy += randomGenerator.inRangeIncl(10,20);
    else if (vy <= 0 && vy > -50)
        vy -= randomGenerator.inRangeIncl(10,20);

    double speed = sqrt(vx*vx + vy*vy);
	if(speed < MAX_SPEED)
	{
		vx *= 1.11;
		vy *= 1.11;
	}
    else
    {
        vx *= MAX_SPEED / speed;
        vy *= MAX_SPEED / speed;
    }
	leftPlayer.increaseSpeed();
	rightPlayer.increaseSpeed();
}

void Ball::showSparks(PlayerPosition pos)
{
    int sparkleX = 0;
    if(pos == PP_LEFT)
        sparkleX = leftPlayer.x + leftPlayer.getWidth()/2;
    if(pos == PP_RIGHT)
        sparkleX = rightPlayer.x - rightPlayer.getWidth()/2;
	spark = new Sparks(pos, visualObjectProvider->createVisualObject(), sparkleX, y);
}

void Ball::draw()
{
	// draw the spark
	if(spark != 0)
		spark->draw();

	// draw the ball itself
	representation->draw();


	
}

void Ball::getReadyForDraw()
{
	x = int(realX);
	y = int(realY);

	// we need to calculate offsets!
	representation->setOffsetX(x - getWidth()/2);
	representation->setOffsetY(y - getHeight()/2);

	if(spark != 0)
		spark->getReadyForDraw();
}

bool Ball::intersectsWithPlayer(Player &p)
{
	const int radius = 10;

	double circleDistanceX = abs(realX - p.x);
	double circleDistanceY = abs(realY - p.y);

	if (circleDistanceX > (p.getWidth()/2 + radius)) { return false; }
	if (circleDistanceY > (p.getHeight()/2 + radius)) { return false; }

	if (circleDistanceX <= (p.getWidth()/2)) { return true; } 
	if (circleDistanceY <= (p.getHeight()/2)) { return true; }

	double cornerDistance = sqrt((circleDistanceX - p.getWidth()/2/2)*(circleDistanceX - p.getWidth()/2/2) +
								(circleDistanceY - p.getHeight()/2)*(circleDistanceY - p.getHeight()/2));

    return (cornerDistance <= radius);
}


bool Ball::hasScored()
{
	if(x < 0 || x > playfield.getWidth())
		return true;
	else
		return false;
}

PlayerPosition Ball::whoScored()
{
	if(x < 0)
		return PP_LEFT;
	else
		return PP_RIGHT;

}
void Ball::reset()
{
	x = playfield.getWidth()/2;
	y = playfield.getHeight()/2;

	realX = x;
	realY = y;

	vx = randomGenerator.inRangeIncl(150,210);
	vy = randomGenerator.inRangeIncl(40,70);

	if(randomGenerator.chance(50))
		vx *= -1;
	if(randomGenerator.chance(50))
		vy *= -1;

	spark = 0;

    bounces = 0;
}
